home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 3 / Info_Mac_1994-01.iso / Development / 4D Externals / PictBundle 1.1.1 / PictBundle READ ME! next >
Text File  |  1993-09-06  |  23KB  |  418 lines

  1. PictBundle Package for 4th Dimension, version 1.1
  2.  
  3. This package is © 1993 Scott Ribe, all rights reserved.
  4.  
  5. Use of this package requires a $35 shareware fee. This fee entitles one developer to use this package in an unlimited number of applications and to distribute those applications without limit.
  6.  
  7. However, you may distribute it only as part of a complete 4D application. You may not resell it by itself, or as part of any kind of shell or toolkit without the author's express written permission. You may not remove or alter any portion of this notice or the comments.
  8.  
  9. If you decide to use it, please make shareware work and pay the fee. Also please provide me with some address so that I can contact you regarding any upgrades.
  10.  
  11. Scott Ribe
  12.  
  13. CIS 73507,3041
  14.  
  15. 604 Inverness Cliffs
  16. Birmingham, AL 35242
  17.  
  18. INSTALLATION:
  19. ==============
  20.  
  21. You may use 4D External Mover to install this package. However, I have included the resources necessary to make the commands group into two popups in the procedure editor, and as far as I know no version of External Mover prior to External Mover Plus 1.0.2 will move these correctly. If you want the popups and you don't have External Mover Plus 1.0.2 or later, first install with 4D External Mover, then open  your Proc.ext or structure with ResEdit, find the 4DPX name "PictBundle", note its number, and make sure the THM# and FON# resources have the same ID as the 4DPX.
  22.  
  23. CHANGES
  24. ========
  25.  
  26. Version 1.1 adds support for pointer arrays, the GetBdlItemType function. It also adapts to an unannounced change in the way 4D presents version information to externals.
  27.  
  28. Version 1.1.1 corrects a bug introduced in 1.1 that would cause memory corruption when putting a 2D array into a bundle.
  29.  
  30. This update is free to all who have paid their shareware fee.
  31.  
  32. PURPOSE:
  33. ==========
  34.  
  35. First off, obviously, PictBundle is like an enhanced version of the old Array2Picture utilities. PictBundle will work with most 4D data types, all array types, and lets you stuff multiple items into one picture. So you can use it to stuff arrays into a picture field of a record, which is how it got its start in life.
  36.  
  37. Some of the extensions, however, were inspired by 4D version 3 and multiple processes.
  38.  
  39. Process-to-process communications in 4D are complicated by the fact that you can only pass arguments between processes by setting up batches of interprocess variables. This is clumsy at best, and can get really awful if you want to have multiple instances of the same process active at once, or allow multiple calls to a process to queue up. What's really needed is a language facility like C structs or Pascal records that allow data to be grouped into compound types.
  40.  
  41. PictBundle provides enough grouping capability to solve those PPC problems, and to provide a mechanism where 4D procedures can be called with variable argument lists and return multiple values of variable type. If you've ever needed to implement a dispatch mechanism that would "pass through" any type of data, you can do it with this package.
  42.  
  43. PictBundle is intended to mimic in 4D some of the functionality of C structs or Pascal records, in fact I originally called it “PictStruct” instead of “PictBundle”. I changed it because it doesn't quite implement structures.
  44.  
  45. It does:
  46.  
  47. ° allow you to bundle up any number of data items into one logical and physical unit;
  48. ° check typing of read/write operations on the bundle’s elements for consistency.
  49.  
  50. It does not:
  51.  
  52. ° allow you to name the elements contained in a bundle;
  53. ° allow you to write to the values in an arbitrary sequence;
  54. ° enforce typing of bundles themselves.
  55.  
  56. USES:
  57. =====
  58.  
  59. Pass sets of parameters between processes without creating a whole mess of interprocess variables.
  60.  
  61. Post sets of parameters into a queue, where the queue is simply an array of pictures rather than bunches of different arrays.
  62.  
  63. Pass variable sets parameters into a dispatch procedure that just passes them on through without looking at them.
  64.  
  65. Return variable sets parameters from a dispatch procedure depending on what function was actually called.
  66.  
  67. Implement variable call/return syntax for certain procedures.
  68.  
  69. For MSI Access: Bundle up a record's values or a bunch of related data into a single global variable for use with SetBundle or GetBundle. Cleaner than using bunches of global variables. Faster than converting a record into an array of text. Easier than specifying a whole bunch of different bundle arrays.
  70.  
  71. OVERVIEW:
  72. ===========
  73.  
  74. The mental model of how these externals are to be used is something like a box. You take an empty box and toss a bunch of stuff into it. Then you carry it around as long as you want, put on a shelf in the basement, mail it to a friend, or whatever. Eventually, somebody somewhere dumps the contents out of the box and uses them.
  75.  
  76. I would describe the set of externals implemented as complete but primitive. If you want to arrange your box neatly or decorate it, you get to code your own gewgaws.
  77.  
  78. A bundle is a flat block of data consisting of a 14-byte header followed by the data that you have put into it. It works rather like a disk file, in that it has a current read/write position and a logical and physical size. 
  79.  
  80. Writing to it copies the passed value into the bundle at the current read/write position, advances the read/write position, and sets the logical end at the value just written (in other words, unlike a disk file, a write always truncates it to the current position). As more room is needed, the data block is grown in integral multiples of an allocation unit (default is 32 bytes).
  81.  
  82. Reading from it grabs the value at the current position and advances the read/write position. An attempt to read beyond the logical end of the bundle posts an EOF (-39) error. An attempt to read a value that is not the same type as the one at the current position posts a type mismatch error (59). Reads are non-destructive so you can back up and reread the same data as many times as you like. If a type mismatch error occurs, the read does not advance the read/write position, so you cannot keep on reading past an error.
  83.  
  84. Any attempt to access a bundle created with an incompatible version of this package posts a version mismatch error (54). This is version 1.1. Any bundles created with 1.0 can be read by it, but its bundles cannot be read by 1.0.
  85.  
  86. QUICK START:
  87. ==============
  88.  
  89. Basically you create a bundle:
  90.  
  91. C_PICTURE(myBdl1)
  92. myBdl1:=CreateBundle
  93.  
  94. Add data to it:
  95.  
  96. PutBdlLong(myBdl1;$num)
  97. PutBdlString(myBdl1;vCompName)
  98. PutBdlText(myBdl1;[Contacts]comments)
  99. PutBdlArray(myBdl1;as30Phones)
  100.  
  101. Reset the read/write mark:
  102.  
  103. SetBdlPos(myBdl1;0)
  104.  
  105. Pass it to another procedure, store it into a field, write it to disk, whatever. Then read the data back:
  106.  
  107. $num:=GetBdlLong(myBdl1)
  108. vCompName:=GetBdlString(myBdl1)
  109. [Contacts]comments:=GetBdlText(myBdl1)
  110. GetBdlArray(myBdl1;as30Phones)
  111.  
  112. Of course, when writing to the bundle you should check for errors after each write to make sure that you haven't run out memory.
  113.  
  114. PASSING ARGUMENTS:
  115. ===================
  116.  
  117. The values being copied into a bundle, that is the values being passed as the second argument to PutBdlXXX, can be anything: globals, locals, interprocess, constants, fields, or array elements. Values that will be modified must be passed as global variables; including both the Bundle picture variables themselves in nearly all calls, and the arrays in GetBdlArray calls.
  118.  
  119. 4D generally only allows externals to modify the value of global variables. So any value that is going to be modified should be a global variable. In 3.0, this includes interprocess and process variables.
  120.  
  121. In particular, this means that your picture variables used to store bundles should be simple global variables (process or interprocess), not elements of an array, not fields, and not local variables.
  122.  
  123. SetBdlPos(myBdl;0) or (◊myBdl;0) will work. 
  124.  
  125. Neither SetBdlPos([file1]bundle;0) nor SetBdlPos(myBdls{1};0) will work. And StBdlPos($myBdl;0) is not supposed to work. (See discussion below.)
  126.  
  127. NOTE THAT GETTING A VALUE FROM A BUNDLE MODIFIES IT BY ADVANCING THE READ/WRITE POSITION. This means that you must use global variables even when getting values back out of a bundle. If for instance you call GetBdlLong([file1]bundle) you may get a value, but the next attempt to read from [file1]bundle will yield incorrect results.
  128.  
  129. Of course, you can assign a bundle into an array or local variable when you are done using it, and assign it back to a global before using it again. In practice, you could get away with only one global picture variable and always use assignment to and from local picture variables or picture arrays. (Just remember that assigning a picture copies its contents, so you don't want to shuffle around big pictures too much.)
  130.  
  131. My testing indicates that it actually works with bundles stored in local picture variables. But the Externals Kit states "You may pass fields or local variables, however, a changed value can not be returned to them." I don't know whether this is an obsolete restriction that was lifted in some version of 4D, or whether passing locals to a procedure that modifies them will cause trouble. Although I've not seen any problems, I'd suggest following the documented restrictions, unless you feel really brave.
  132.  
  133. Likewise, in 4D 3.0 even GetBdlArray(myBdl;$LocalArray) seems to work, but I wouldn't trust it without exhaustive testing. 
  134.  
  135. REFERENCE:
  136. ===========
  137.  
  138. BundlePackErr -> long
  139. ------------------
  140.  
  141. Returns any error posted by the last call into this package. Mostly Mac memory manager errors. No error is 0. Read past the end of the bundles is EOF (-39). A type mismatch error is 59. A version mismatch error is 54.
  142.  
  143. Under 3.0 returns only the error posted by the last call into this package from the same process.
  144.  
  145. CreateBundle -> picture
  146. -----------------
  147.  
  148. Creates and returns a bundle with a properly initialized header and no data. (14 bytes of header and 18 bytes of preallocated data space.) MUST be called before using the other commands in this package.
  149.  
  150. Passing some picture value not created this way can cause disastrous results.
  151.  
  152. ClearBundle(picture)
  153. --------------
  154.  
  155. Clears the bundle to a properly initialized header and no data. Releases all but the minimum 18 bytes of data space. SHOULD NEVER be called with a picture of any type other than one created by CreateBundle.
  156.  
  157. If you are looking for maximum speed and repeatedly building a bundle that is large or has many elements, don't clear it between uses. Just SetBdlPos(aBdl;0) so that the memory will not be released, and when you write to it again it won't need to be resized. 
  158.  
  159. Of course, the converse is true. If you've written a large amount of data into a bundle and expect to reuse it with only a small amount of data, you should ClearBundle(aBdl) to release the memory.
  160.  
  161. CopyBundle(picture) -> picture
  162.  
  163. Gotcha! There is no such command provided in the package. Just use:
  164.  
  165. aBdl2:=aBdl1
  166.  
  167. See, this is why it's good to read the documentation!
  168.  
  169. GetBdlVersion(picture) -> long
  170. --------------------
  171.  
  172. Returns the version of these externals used to create this bundle. The version is carried with the bundle in case I have to change the data format in a future release. Version 1.0 returns a 1; version 1.1 returns a 2.
  173.  
  174. CompareBdls(picture;picture) -> long
  175. ---------------------
  176.  
  177. Returns 1 if the two bundles are byte for byte identical, 0 otherwise.
  178.  
  179. This can be used on any picture value, including those created as a real picture or those created by other externals. But any difference at all, including just a time stamp or unique id buried in a comment, will cause the two to be considered not equal. So before using this on a non-bundle pict, you'd better have an understanding of the structure in the pict.
  180.  
  181. SetBdlBlockSize(picture;long)
  182. --------------------
  183.  
  184. Bundles, like disk files, are allocated in integral blocks in an attempt to avoid constantly resizing the data handle and grossly fragmenting memory when adding small variables. This sets the allocation size for the bundle. The default is 32 bytes.
  185.  
  186. SetBdlPos(picture;long)
  187. --------------
  188.  
  189. Sets the read/write position. Note that a write ALWAYS truncates the bundle at the end of the written data.
  190.  
  191. Most commonly used to reset the mark to begin reading after writing:
  192.  
  193. SetBdlPos(aBdl;0)
  194.  
  195. Note that bundles maintain their read/write position when saved to a file, etc. So after writing you must always reset the mark before reading.
  196.  
  197. GetBdlPos(picture) -> long
  198. ----------------
  199.  
  200. Returns the current read/write position of the bundle.
  201.  
  202. GetBdlLogLen(picture) -> long
  203. -------------------
  204.  
  205. Returns the current logical length of the bundle, that is the length of the data that has actually been written into the bundle.
  206.  
  207. To append to a bundle after backing up and reading, you can:
  208.  
  209. SetBdlPos(aBdl;GetBdlLogLen(aBdl))
  210.  
  211. GetBdlPhyLen(picture) -> long
  212. -------------------
  213.  
  214. Returns the physical size of the handle. Is equal to the logical size plus the size of the header information plus any fraction of an allocation block that has not been used.
  215.  
  216. This just returns the handle size, and so can be used on any picture value created by any external.
  217.  
  218. GetBdlItemType(picture) -> long
  219.  
  220. Returns the type of the item at the current read/write position. If the position is at the end of the array returns 5 (undefined) and posts an EOF error.
  221.  
  222. PutBdlShort(picture;short)
  223. ---------------
  224.  
  225. Writes the short integer value into the bundle at the current read/write position.
  226.  
  227. GetBdlShort(picture) -> short
  228. ------------------
  229.  
  230. Returns the short integer value from  the bundle's current read/write position.
  231.  
  232. PutBdlLong(picture;long)
  233. ---------------
  234.  
  235. Writes the long integer value into the bundle at the current read/write position.
  236.  
  237. GetBdlLong(picture) -> long
  238. -----------------
  239.  
  240. Returns the long integer value from  the bundle's current read/write position.
  241.  
  242. PutBdlReal(picture;real)
  243. ---------------
  244.  
  245. Writes the real value into the bundle at the current read/write position.
  246.  
  247. GetBdlReal(picture) -> real
  248. -----------------
  249.  
  250. Returns the real value from  the bundle's current read/write position.
  251.  
  252. PutBdlDate(picture;date)
  253. ---------------
  254.  
  255. Writes the date value into the bundle at the current read/write position.
  256.  
  257. GetBdlDate(picture) -> date
  258. -----------------
  259.  
  260. Returns the date value from  the bundle's current read/write position.
  261.  
  262. PutBdlString(picture;string)
  263. -----------------
  264.  
  265. Writes the string value into the bundle at the current read/write position.
  266.  
  267. GetBdlString(picture) -> string
  268. -------------------
  269.  
  270. Returns the string value from  the bundle's current read/write position.
  271.  
  272. PutBdlText(picture;text)
  273. ---------------
  274.  
  275. Writes the text value into the bundle at the current read/write position.
  276.  
  277. GetBdlText(picture) -> text
  278. -----------------
  279.  
  280. Returns the text value from  the bundle's current read/write position.
  281.  
  282. PutBdlPict(picture;picture)
  283. ---------------
  284.  
  285. Writes the pict value into the bundle at the current read/write position.
  286.  
  287. Note that since bundles are just picts, this command lets you nest bundles inside each other.
  288.  
  289. GetBdlPict(picture) -> picture
  290. -----------------
  291.  
  292. Returns the pict value from  the bundle's current read/write position.
  293.  
  294. PutBdlArray(picture;array)
  295. ----------------
  296.  
  297. Writes the array (and the index of its currently selected element) into the bundle at the current read/write position.
  298.  
  299. This works for all array types, 1 or 2 dimension, any size up to the limit of available memory. As of version 1.1 it works for pointer arrays as well, but see the warning below.
  300.  
  301. Note also that 4D's conventions regarding 2D arrays is followed strictly. A column of a 2D array is itself precisely an array of the base type. So:
  302.  
  303. ARRAY TEXT(myArray;5;10)
  304. PutBdlArray(myBdl;myArray{5})
  305.  
  306. is perfectly legal.
  307.  
  308. Both the type and ordinality of the array are considered part of the value's type for error checking when reading the array back. For string arrays the string size is considered part of the type as well, but a size mismatch does not generate an error.
  309.  
  310. GetBdlArray(picture;array)
  311. ----------------
  312.  
  313. Reads the array (and the index of its currently selected element) from  the bundle's current read/write position.
  314.  
  315. This works for all array types, 1 or 2 dimension, any size up to the limit of available memory. The procedure will redimension the array as needed. As of version 1.1 it works for pointer arrays as well, but see the warning below.
  316.  
  317. Note also that 4D's conventions regarding 2D arrays is followed strictly. A column of a 2D array is itself precisely an array of the base type. So:
  318.  
  319. ARRAY TEXT(myArray;5;10)
  320. GetBdlArray(myBdl;myArray{5})
  321.  
  322. is perfectly legal.
  323.  
  324. Both the type and ordinality of the array are considered part of the value's type for error checking when reading the array back.
  325.  
  326. If you write from a string array and then read back into a string array whose elements are of a different size, no error will be posted. Instead the strings in the bundle will be properly read into the new array: truncated (for a shorter destination) or left-aligned (for a longer destination). The read operation will be much faster if the original source and destination arrays have elements of the same size, because I can just use a single BlockMove instead of handling each string separately.
  327.  
  328. If there is not enough memory available to build the new array, the existing array will be destroyed before that is discovered. The procedure will in that case create a 0-dimensioned array with 1 null element and return it, so that 4D will not crash on a subsequent access to the array. (Of course the error will be available via BundlePackErr as always.)
  329.  
  330. ===================================================================
  331. POINTER ARRAY WARNING
  332. ===================================================================
  333.  
  334. The format and contents of pointers is not documented by ACI. Pointers cannot be passed to or returned from externals. It just so happens that 4D uses a standard format for all arrays, and will allow an array of pointers to be passed to an external. So I can simply grab the data and copy it.
  335.  
  336. However, since the meaning of the data is not defined, I cannot guarantee all aspects of the semantics of passing pointers through bundles.
  337.  
  338. An array of pointers to process variables unpacked in the same process in which it was packed will result in the pointers pointing the same instances of the variables. If unpacked in a different process, they will point to the instances of those variables in the process in which they are unpacked. (Just like passing a pointer via an interprocess variable.)
  339.  
  340. What happens if you unpack a pointer array in a different instance of the application? In other words if you send it across the network to another workstation, or save it to file or disk and retrieve it later after the user has quit and restart the program? I have no idea; I don't intend to find out; and I suggest strongly that you don't try to do this.
  341.  
  342. ===================================================================
  343. FOR EXPERTS ONLY!!!
  344. ===================================================================
  345.  
  346. To all you out there who know what you're doing, please don't take offense at the tone of the comments for the following three commands. This stuff really is for knowledgeable C programmers and I just wanted to very strongly discourage folks from getting in over their heads...
  347.  
  348. PutBdlHandle(picture;long)
  349. -----------------
  350.  
  351. Assumes that the long integer passed is actually a handle created by some other external package, takes the data in that handle and copies it into the bundle, typed as a PICT.
  352.  
  353. These commands:
  354.  
  355. PutBdlHandle(myBdl;myHdl1)
  356. SetBdlPos(myBdl;0)
  357. myHdl2:=GetBdlHandle(myBdl)
  358.  
  359. cause myHdl2 to be a handle to a copy of the data in myHdl1.
  360.  
  361. You can follow PutBdlHandle with GetBdlPict, or PutBdlPict with GetBdlHandle, and no error will be generated, the position will be advanced, everything will be OK, and you can keep reading. I just can't think of a reason WHY you'd want to do that, and don't know what the heck you'd do with the resulting data??
  362.  
  363. You should know EXACTLY what you are doing and what the structure of the data in your handle is before calling this procedure. Note in particular that if the handle contains other handles or pointers, then of course only the handles or pointers, not the data they reference, will be copied. Therefore if you use this to copy a handle that is subsequently disposed by its creator, your copy will contain invalid references, and using it will probably crash your system. (Yep, it can be really ugly when one half of a pair of Siamese twin handles dies...)
  364.  
  365. DON'T ASK ME FOR HELP WITH THE THREE HANDLE-ORIENTED COMMANDS. I AM NOT A SUBSTITUTE FOR INSIDE MACINTOSH.
  366.  
  367. GetBdlHandle(picture) -> long
  368. -------------------
  369.  
  370. Requires that the next item to be read from the bundle is of type PICT, allocates a handle for the data, copies the data into it, and returns the handle.
  371.  
  372. The pict data could have been put into the bundle using either PutBdlPict or PutBdlHandle. (But only PutBdlHandle really makes sense.)
  373.  
  374. If a4dLong contains a handle then:
  375.  
  376. a4dLong := GetBdlHandle(myBundle)
  377.  
  378. Does NOT dispose the handle currently in a4dLong, you just lose track of it. Once again, you should know what you're doing before you start messing around with handles.
  379.  
  380. DisposBdlHandle(long)
  381. ---------------
  382.  
  383. If you have to ask what this does, you should not ever consider calling it under any circumstances!
  384.  
  385. Source code is (roughly, after factoring out my macros for accessing and casting 4D package arguments):
  386.  
  387. DisposHandle (*(Handle *)(PackArgs[1]));
  388.  
  389. If you don't understand that, go back  and reread the preceding warnings about messing with the handle commands.
  390.  
  391. TECHIE STUFF:
  392. =============
  393.  
  394. If you're a C programmer and you know how to write 4D externals and you wish to write your own externals to work with my bundles, here's what you need to know:
  395.  
  396. typedef struct
  397. {    struct
  398.     {    long pBlock, pEnd, pPos;
  399.         char bVersion, bOptions;
  400.     } header;
  401.     char pData;
  402. } PictStruct;
  403. /*
  404.     pEnd    length of actual data, may be shorter than pict size 
  405.             (like a file's logical size as opposed to physical)
  406.     pPos    0-based offset of next byte to be read or written
  407.     pBlock    allocation unit to use when growing the data handle
  408.     pData    first character of data
  409.     bVersion    version of the externals that created this bundle
  410.     bOptions    8 bits for bundle options, currently there are none
  411. */
  412.  
  413. Just follow these two rules and you should be able to "share" bundles with me:
  414.  
  415. 1) You should read/write your own data types in precisely matched pairs. Don't try to read data that I write, and don't try to write data for me to read.
  416.  
  417. 2) Make sure to maintain pEnd and pPos consistently.
  418.